/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *****************************************************************************/
package picounit.impl;

import org.picocontainer.ComponentAdapter;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.Parameter;
import org.picocontainer.PicoContainer;
import org.picocontainer.PicoVisitor;
import org.picocontainer.defaults.DefaultPicoContainer;
import org.picocontainer.defaults.DuplicateComponentKeyRegistrationException;

import picounit.features.registry.SilentlyReplacingMutablePicoContainer;
import previous.picounit.DelegateVerify;
import previous.picounit.Should;
import previous.picounit.Test;
import previous.picounit.Verify;

public class SilentlyReplacingMutablePicoContainerTest implements Test {
	private final SilentlyReplacingMutablePicoContainer silentlyReplacingMutablePicoContainer;

	private final MutablePicoContainer delegate;

	private final Class componentImplementation = Class.class;

	private final Object componentKey = new Object();

	private final Object componentInstance = new Object();

	private final Parameter[] parameters = new Parameter[0];

	private final ComponentAdapter componentAdapter;

	public SilentlyReplacingMutablePicoContainerTest(MutablePicoContainer mockPicoContainer,
		ComponentAdapter mockComponentAdapter) {

		this.delegate = mockPicoContainer;
		this.componentAdapter = mockComponentAdapter;
		this.silentlyReplacingMutablePicoContainer =
			new SilentlyReplacingMutablePicoContainer(mockPicoContainer);
	}
	
	public void shouldDelegateRegistrationMethods(DelegateVerify should) {
		should.delegateTo(delegate.registerComponent(componentAdapter))
			.whenCalling(silentlyReplacingMutablePicoContainer.registerComponent(componentAdapter));
		
		should.delegateTo(delegate.registerComponentImplementation(componentImplementation))
			.whenCalling(silentlyReplacingMutablePicoContainer.registerComponentImplementation(componentImplementation));
		
		should.delegateTo(delegate.registerComponentImplementation(componentKey, componentImplementation))
			.whenCalling(silentlyReplacingMutablePicoContainer.registerComponentImplementation(componentKey, componentImplementation));
		
		should.delegateTo(delegate.registerComponentImplementation(componentKey, componentImplementation, parameters))
			.whenCalling(silentlyReplacingMutablePicoContainer.registerComponentImplementation(componentKey, componentImplementation, parameters));
		
		should.delegateTo(delegate.registerComponentInstance(componentInstance))
			.whenCalling(silentlyReplacingMutablePicoContainer.registerComponentInstance(componentInstance));

		should.delegateTo(delegate.registerComponentInstance(componentKey, componentInstance))
			.whenCalling(silentlyReplacingMutablePicoContainer.registerComponentInstance(componentKey, componentInstance));
	}
	
	public void shouldUnregisterWhenRegisterComponentAdapterThrowsDuplicateComponentKeyRegistrationException(Should should, Verify verify) {
		should.call(delegate.registerComponent(componentAdapter))
			.andRaise(new DuplicateComponentKeyRegistrationException(componentKey));
		should.call(componentAdapter.getComponentKey()).andReturn(componentKey);
		should.call(delegate.unregisterComponent(componentKey));
		should.call(delegate.registerComponent(componentAdapter)).andReturn(componentAdapter);

		should.expectAboveWhenTheFollowingOccurs();

		verify.that(silentlyReplacingMutablePicoContainer.registerComponent(componentAdapter))
			.isTheSameAs(componentAdapter);
	}
	
	public void shouldUnregisterWhenRegisterImplementationThrowsDuplicateComponentKeyRegistrationException(Should should, Verify verify) {
		should.call(delegate.registerComponentImplementation(componentImplementation))
			.andRaise(new DuplicateComponentKeyRegistrationException(componentImplementation));
		should.call(delegate.unregisterComponent(componentImplementation));
		should.call(delegate.registerComponentImplementation(componentImplementation)).andReturn(componentAdapter);

		should.expectAboveWhenTheFollowingOccurs();

		verify.that(silentlyReplacingMutablePicoContainer.registerComponentImplementation(componentImplementation))
			.isTheSameAs(componentAdapter);
	}
	
	public void shouldUnregisterWhenRegisterKeyImplementationThrowsDuplicateComponentKeyRegistrationException(Should should, Verify verify) {
		should.call(delegate.registerComponentImplementation(componentKey, componentImplementation))
			.andRaise(new DuplicateComponentKeyRegistrationException(componentKey));
		should.call(delegate.unregisterComponent(componentKey));
		should.call(delegate.registerComponentImplementation(componentKey, componentImplementation)).andReturn(componentAdapter);

		should.expectAboveWhenTheFollowingOccurs();

		verify.that(silentlyReplacingMutablePicoContainer.registerComponentImplementation(componentKey, componentImplementation))
			.isTheSameAs(componentAdapter);
	}

	public void shouldUnregisterWhenRegisterKeyImplementationThrowsDuplicateComponentKeyParametersRegistrationException(Should should, Verify verify) {
		should.call(delegate.registerComponentImplementation(componentKey, componentImplementation, parameters))
			.andRaise(new DuplicateComponentKeyRegistrationException(componentKey));
		should.call(delegate.unregisterComponent(componentKey));
		should.call(delegate.registerComponentImplementation(componentKey, componentImplementation, parameters)).andReturn(componentAdapter);

		should.expectAboveWhenTheFollowingOccurs();

		verify.that(silentlyReplacingMutablePicoContainer.registerComponentImplementation(componentKey, componentImplementation, parameters))
			.isTheSameAs(componentAdapter);
	}
	
	public void shouldUnregisterWhenRegisterInstanceThrowsDuplicateComponentKeyRegistrationException(Should should, Verify verify) {
		should.call(delegate.registerComponentInstance(componentInstance))
			.andRaise(new DuplicateComponentKeyRegistrationException(componentInstance));
		should.call(delegate.unregisterComponent(componentInstance));
		should.call(delegate.registerComponentInstance(componentInstance)).andReturn(componentAdapter);

		should.expectAboveWhenTheFollowingOccurs();

		verify.that(silentlyReplacingMutablePicoContainer.registerComponentInstance(componentInstance))
			.isTheSameAs(componentAdapter);
	}

	public void shouldUnregisterWhenRegisterKeyInstanceThrowsDuplicateComponentKeyRegistrationException(Should should, Verify verify) {
		should.call(delegate.registerComponentInstance(componentKey, componentInstance))
			.andRaise(new DuplicateComponentKeyRegistrationException(componentInstance));
		should.call(delegate.unregisterComponent(componentKey));
		should.call(delegate.registerComponentInstance(componentKey, componentInstance)).andReturn(componentAdapter);

		should.expectAboveWhenTheFollowingOccurs();

		verify.that(silentlyReplacingMutablePicoContainer.registerComponentInstance(componentKey, componentInstance))
			.isTheSameAs(componentAdapter);
	}

	public void shouldDelegateNonRegistrationMethods(DelegateVerify should) {
		should.delegateTo(delegate.unregisterComponent(componentKey))
			.whenCalling(silentlyReplacingMutablePicoContainer.unregisterComponent(componentKey));

		should.delegateTo(delegate.unregisterComponentByInstance(componentInstance))
			.whenCalling(silentlyReplacingMutablePicoContainer.unregisterComponentByInstance(componentInstance));
		
		// TODO: Consider wrapping the child container with another Silently Replacing one
		should.delegateTo(delegate.makeChildContainer())
			.whenCalling(silentlyReplacingMutablePicoContainer.makeChildContainer());

		PicoContainer childContainer = new DefaultPicoContainer();
		should.delegateTo(delegate.addChildContainer(childContainer))
			.whenCalling(silentlyReplacingMutablePicoContainer.addChildContainer(childContainer));

		should.delegateTo(delegate.removeChildContainer(childContainer))
			.whenCalling(silentlyReplacingMutablePicoContainer.removeChildContainer(childContainer));

		should.delegateTo(delegate.getComponentInstance(componentKey))
			.whenCalling(silentlyReplacingMutablePicoContainer.getComponentInstance(componentKey));
	
		Class componentType = Class.class;
		should.delegateTo(delegate.getComponentInstanceOfType(componentType))
			.whenCalling(silentlyReplacingMutablePicoContainer.getComponentInstanceOfType(componentType));

		should.delegateTo(delegate.getComponentInstances())
			.whenCalling(silentlyReplacingMutablePicoContainer.getComponentInstances());
		
		should.delegateTo(delegate.getParent())
			.whenCalling(silentlyReplacingMutablePicoContainer.getParent());
		
		should.delegateTo(delegate.getComponentAdapter(componentKey))
			.whenCalling(silentlyReplacingMutablePicoContainer.getComponentAdapter(componentKey));
		
		should.delegateTo(delegate.getComponentAdapterOfType(componentType))
			.whenCalling(silentlyReplacingMutablePicoContainer.getComponentAdapterOfType(componentType));
		
		should.delegateTo(delegate.getComponentAdapters())
			.whenCalling(silentlyReplacingMutablePicoContainer.getComponentAdapters());
		
		should.delegateTo(delegate.getComponentAdaptersOfType(componentType))
			.whenCalling(silentlyReplacingMutablePicoContainer.getComponentAdaptersOfType(componentType));
		
		should.delegateTo(delegate.getComponentInstancesOfType(componentType))
			.whenCalling(silentlyReplacingMutablePicoContainer.getComponentInstancesOfType(componentType));
	}
	
	@SuppressWarnings("deprecation")
	public void shouldDelegateVerify(Should should) {
		delegate.verify();
		
		should.expectAboveWhenTheFollowingOccurs();
		
		silentlyReplacingMutablePicoContainer.verify();
	}
	
	public void shouldDelegateAccept(Should should, PicoVisitor mockPicoVisitor) {
		delegate.accept(mockPicoVisitor);

		should.expectAboveWhenTheFollowingOccurs();

		silentlyReplacingMutablePicoContainer.accept(mockPicoVisitor);
	}
	
	public void shouldDelegateStart(Should should) {
		delegate.start();
		
		should.expectAboveWhenTheFollowingOccurs();
		
		silentlyReplacingMutablePicoContainer.start();
	}
	
	public void shouldDelegateStop(Should should) {
		delegate.stop();
		
		should.expectAboveWhenTheFollowingOccurs();
		
		silentlyReplacingMutablePicoContainer.stop();
	}
	
	public void shouldDelegateDispose(Should should) {
		delegate.dispose();
		
		should.expectAboveWhenTheFollowingOccurs();
		
		silentlyReplacingMutablePicoContainer.dispose();
	}
}
